package com.ysd.lis.service.qc.impl;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.ysd.common.Result;
import com.ysd.lis.entity.inti.IntInstrResult;
import com.ysd.lis.entity.lab.LabInstrSetup;
import com.ysd.lis.entity.lab.LabItemDict;
import com.ysd.lis.entity.lab.LabRptresult;
import com.ysd.lis.entity.qc.*;
import com.ysd.lis.mapper.lab.LabRptresultMapper;
import com.ysd.lis.mapper.qc.*;
import com.ysd.lis.request.QcResultTimeParam;
import com.ysd.lis.request.qc.*;
import com.ysd.lis.service.qc.QcMaterialItemResultService;
import com.ysd.lis.util.qcrule.*;
import com.ysd.util.ToolsUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Timestamp;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.*;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.groupingBy;


/**
 * <p>
 * cold_chain.t_user 服务实现类
 * </p>
 *
 * @author peng
 * @since 2024-01-08
 */
@Service
@Slf4j
public class QcMaterialItemResultServiceImpl extends ServiceImpl<QcMaterialItemResultMapper, QcMaterialItemResult> implements QcMaterialItemResultService {

    @Autowired
    QcMaterialItemResultMapper qcMaterialItemResultMapper;
    @Autowired
    QcMaterialItemMapper qcMaterialItemMapper;
    @Autowired
    QcMaterialItemTargetMapper qcMaterialItemTargetMapper;
    @Autowired
    QcMaterialItemFixedMapper qcMaterialItemFixedMapper;
    @Autowired
    QcInstrItemRuleMapper qcInstrItemRuleMapper;
    @Autowired
    QcRuleMapper qcRuleMapper;

    /**
     * 查询某一天结果数据
     *
     * @param qcResultTimeParam
     * @return
     */
    @Override
    public Result getQcMaterialItemResultData(QcResultTimeParam qcResultTimeParam) {
        // 查询项目信息
        MPJLambdaWrapper<QcMaterialItem> queryWrapper = new MPJLambdaWrapper<>();
        queryWrapper
                .selectAll(QcMaterialItem.class)
                .selectAs("ld", LabItemDict::getItemNo, QcMaterialItem::getItemDictCode)
                .selectAs("ld", LabItemDict::getItemNa, QcMaterialItem::getItemDictName)
                .selectAs("ld", LabItemDict::getItemEnCode, QcMaterialItem::getItemEnCode)
                .selectAs(QcMaterialItemTarget::getMean, QcMaterialItem::getMean)
                .selectAs(QcMaterialItemTarget::getSd, QcMaterialItem::getSd)
                .leftJoin(LabItemDict.class, "ld", LabItemDict::getId, QcMaterialItem::getItemDictId)
                .leftJoin(QcMaterialItemTarget.class, QcMaterialItemTarget::getMaterialId, QcMaterialItem::getId)
                .eq(QcMaterialItem::getMaterialId, qcResultTimeParam.getMaterialId())
                .eq(QcMaterialItem::getDelFlag, 0)
                .orderByAsc(QcMaterialItem::getSeq);
        // 项目数据信息
        List<QcMaterialItem> itemList = qcMaterialItemMapper.selectJoinList(QcMaterialItem.class, queryWrapper);
        List<String> collected = itemList.stream().map(item -> item.getId()).collect(Collectors.toList());
        if (collected.size() == 0) {
            return Result.succ(1, "暂无数据!", null);
        }
        List<Map<String, Object>> resultList = new ArrayList<>();
        // 查询结果数据
        Date today;
        try {
            today = DateUtils.parseDate(qcResultTimeParam.getQcDate(), "yyyy-MM-dd");
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
        MPJLambdaWrapper<QcMaterialItemResult> lambdaQueryWrapper = new MPJLambdaWrapper();
        lambdaQueryWrapper.selectAll(QcMaterialItemResult.class)
                .selectAs("tg", QcMaterialItemTarget::getCv, QcMaterialItemResult::getCv)
                .leftJoin(QcMaterialItemTarget.class, "tg", QcMaterialItemTarget::getId, QcMaterialItemResult::getMaterialTargetId)
                .in(QcMaterialItemResult::getMaterialItemId, collected)
                .ge(QcMaterialItemResult::getQcDate, DateUtil.beginOfDay(today).toTimestamp())
                .le(QcMaterialItemResult::getQcDate, DateUtil.endOfDay(today).toTimestamp());
        List<QcMaterialItemResult> qcMaterialItemResults = qcMaterialItemResultMapper.selectList(lambdaQueryWrapper);
        Map<String, List<QcMaterialItemResult>> collect = qcMaterialItemResults.stream().collect(groupingBy(QcMaterialItemResult::getMaterialItemId));
        QcMaterial qcMaterial = qcMaterialMapper.selectById(qcResultTimeParam.getMaterialId());
        itemList.forEach(item -> {
            Map<String, Object> map = new HashMap<>();
            map.put("ans", item);
            map.put("qcMaterial", qcMaterial);
            map.put("ansSequenceResults", collect.containsKey(item.getId()) ? collect.get(item.getId()) : new ArrayList<>());
            resultList.add(map);
        });
        return Result.succ(1, "查询成功", resultList);
    }

    @Override
    public Result addQcMaterialItemResult(QcResultAddParam qcResultAddParam) {
        boolean b = buildMaterialItemResult(qcResultAddParam);
        if (b) {
            return Result.succ(1, "添加成功！", b);
        } else {
            return Result.fail("添加失败！");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean buildMaterialItemResult(QcResultAddParam qcResultAddParam) {
        List<QcMaterialItemResult> list = new ArrayList<>();
        //查出结果为空的数据
        List<String> delResultIdList = new ArrayList<>();
        //查定量
        List<QcSequenceResultAddParam> dlList = qcResultAddParam.getAnsWithResults().stream().filter(a -> a.getItemDefine().equals("dl")).collect(Collectors.toList());
        List<String> dlcollected = dlList.stream().map(item -> item.getMaterialItemId()).collect(Collectors.toList());
        if(ToolsUtils.isNotEmpty(dlcollected)){
            //查询靶值SD值
            LambdaQueryWrapper<QcMaterialItemTarget> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(QcMaterialItemTarget::getDelFlag, 0)
                    .in(QcMaterialItemTarget::getMaterialItemId, dlcollected)
                    //.le(QcMaterialItemTarget::getEnableDate, new Timestamp(new Date().getTime()))
                    //.and(wrapper -> wrapper.isNull(QcMaterialItemTarget::getStopDate).or().ge(QcMaterialItemTarget::getStopDate, new Timestamp(new Date().getTime())))
                    .orderByAsc(QcMaterialItemTarget::getSeq);
            List<QcMaterialItemTarget> targetList = qcMaterialItemTargetMapper.selectList(queryWrapper);
            dlList.stream().forEach(item -> {
                item.getIqcSequenceResults().stream().forEach(item1 -> {
//                    if (StringUtils.isNotBlank(item1.getResult()) && StringUtils.isNotBlank(item1.getId())) {
//                        item1.setNumResult(Double.valueOf(item1.getResult()));
//                        Double sd = item1.getSd();
//                        Double mean = item1.getMean();
//                        if(ToolsUtils.isNotEmpty(sd)&& ToolsUtils.isNotEmpty(mean)){
//                            item1.setZ(sd==0?0:(item1.getNumResult() - mean) / sd);
//                        }
//                        list.add(item1);
//                    } else

                    if (StringUtils.isNotBlank(item1.getResult())) {
                        item1.setMaterialItemId(item.getMaterialItemId());
                        item1.setNumResult(Double.valueOf(item1.getResult()));

                        Optional<QcMaterialItemTarget> first = targetList.stream().filter(a -> a.getMaterialItemId().equals(item.getMaterialItemId()) && (a.getEnableDate().compareTo(item1.getQcDate()) <= 0) && (ToolsUtils.isEmpty(a.getStopDate()) || (ToolsUtils.isNotEmpty(a.getStopDate()) && a.getStopDate().after(item1.getQcDate())))).findFirst();


                        if (first.isPresent()) {
                            QcMaterialItemTarget qcMaterialItemTarget = first.get();
                            item1.setMaterialTargetId(qcMaterialItemTarget.getId());
                            Double mean = qcMaterialItemTarget.getMean();
                            Double sd = qcMaterialItemTarget.getSd();
                            item1.setMean(mean);
                            item1.setSd(sd);
                            item1.setZ(sd==0?0:(item1.getNumResult() - mean) / sd);
                        }
                        list.add(item1);
                    }else if(StringUtils.isNotBlank(item1.getId())&&StringUtils.isBlank(item1.getResult())){
                        delResultIdList.add(item1.getId());
                    }
                });
            });
            if(CollectionUtils.isNotEmpty(delResultIdList)){
                this.removeByIds(delResultIdList);
            }
            boolean b = this.saveOrUpdateBatch(list);

            Date today = DateUtil.parse(qcResultAddParam.getQcDate(), "yyyy-MM-dd");
            Timestamp beginDate = DateUtil.beginOfDay(today).toTimestamp();
            Timestamp endDate = DateUtil.endOfDay(today).toTimestamp();
            List<String> itemIdList = list.stream().map(QcMaterialItemResult::getMaterialItemId).distinct().collect(Collectors.toList());
            for(String mItemId : itemIdList){
                QcMaterialItemResult result = list.stream().filter(m -> m.getMaterialItemId().equals(mItemId)).findFirst().get();
                this.calcQcRuleManyAsync(result.getMaterialItemId(),result.getQcItemId(),result.getInstrId(),beginDate,endDate);
            }
        }



        //查定性
        List<QcSequenceResultAddParam> dxList = qcResultAddParam.getAnsWithResults().stream().filter(a -> a.getItemDefine().equals("dx")).collect(Collectors.toList());
        List<String> dxCollected = dxList.stream().map(item -> item.getMaterialItemId()).collect(Collectors.toList());


        if(ToolsUtils.isNotEmpty(dxCollected)){
            //查询靶值SD值
            LambdaQueryWrapper<QcMaterialItemFixed> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(QcMaterialItemFixed::getDelFlag, 0)
                    .in(QcMaterialItemFixed::getMaterialItemId, dxCollected)
                    .orderByAsc(QcMaterialItemFixed::getSeq);
            List<QcMaterialItemFixed> fixedList = qcMaterialItemFixedMapper.selectList(queryWrapper);
            dxList.stream().forEach(item -> {
                item.getIqcSequenceResults().stream().forEach(item1 -> {
                    if (StringUtils.isNotBlank(item1.getResult()) && StringUtils.isNotBlank(item1.getId())) {
                        if (CollectionUtils.isNotEmpty(fixedList)) {
                            Optional<QcMaterialItemFixed> first = fixedList.stream()
                                    .filter(a -> a.getEnableDate().compareTo(item1.getQcDate()) <= 0
                                            && (ToolsUtils.isEmpty(a.getStopDate()) || (ToolsUtils.isNotEmpty(a.getStopDate()) && a.getStopDate().after(item1.getQcDate())))
                                            && a.getResult().equals(item1.getResult())).findFirst();
                            if(first.isPresent()){
                                QcMaterialItemFixed itemFixed = first.get();
                                item1.setNumResult(itemFixed.getNumResult());
                                item1.setIsAlarm(itemFixed.getIsAlarm());
                                item1.setIsOutControl(itemFixed.getIsOutControl());
                                item1.setMaterialTargetId(itemFixed.getId());
                                item1.setZ(itemFixed.getZ());
                            }
                        }
                        list.add(item1);
                    } else if (StringUtils.isNotBlank(item1.getResult()) && StringUtils.isBlank(item1.getId())) {
                        item1.setMaterialItemId(item.getMaterialItemId());
                        Optional<QcMaterialItemFixed> first = fixedList.stream()
                                .filter(a -> a.getEnableDate().compareTo(item1.getQcDate()) <= 0
                                        && (ToolsUtils.isEmpty(a.getStopDate()) || (ToolsUtils.isNotEmpty(a.getStopDate()) && a.getStopDate().after(item1.getQcDate())))
                                        && a.getResult().equals(item1.getResult())).findFirst();


                        if (first.isPresent()) {
                            QcMaterialItemFixed itemFixed = first.get();
                            item1.setMaterialTargetId(itemFixed.getId());
                            item1.setNumResult(itemFixed.getNumResult());
                            item1.setIsAlarm(itemFixed.getIsAlarm());
                            item1.setIsOutControl(itemFixed.getIsOutControl());
                            item1.setMaterialTargetId(itemFixed.getId());
                            item1.setZ(itemFixed.getZ());
                        }
                        list.add(item1);
                    }else if(StringUtils.isNotBlank(item1.getId())&&StringUtils.isBlank(item1.getResult())){
                        delResultIdList.add(item1.getId());
                    }
                });
            });
            if(CollectionUtils.isNotEmpty(delResultIdList)){
                this.removeByIds(delResultIdList);
            }
            boolean b = this.saveOrUpdateBatch(list);

        }
        return true;
    }

    @Override
    public Result getQcResultByMonth(QcPageDto dto) {
        Date today = DateUtil.parse(dto.getQcMonth(), "yyyy-MM");
        MPJLambdaWrapper<QcMaterialItemResult> queryWrapper = new MPJLambdaWrapper<>();
        queryWrapper.selectAll(QcMaterialItemResult.class)
                .selectAs("tg", QcMaterialItemTarget::getCv, QcMaterialItemResult::getCv)
                .leftJoin(QcMaterialItemTarget.class, "tg", QcMaterialItemTarget::getId, QcMaterialItemResult::getMaterialTargetId)
                .eq(QcMaterialItemResult::getDelFlag, 0)
                .eq(QcMaterialItemResult::getMaterialItemId, dto.getPid())
                .ge(QcMaterialItemResult::getQcDate, DateUtil.beginOfMonth(today).toTimestamp())
                .le(QcMaterialItemResult::getQcDate, DateUtil.endOfMonth(today).toTimestamp());
        List<QcMaterialItemResult> materialItemResultList = qcMaterialItemResultMapper.selectList(queryWrapper);
        Map<Timestamp, List<QcMaterialItemResult>> collect = materialItemResultList.stream().collect(groupingBy(QcMaterialItemResult::getQcDate));
        List<Map<String, Object>> dayResults = new ArrayList<>();
        collect.forEach((qcDate, resultList) -> {
            Map<String, Object> map = new HashMap<>();
            map.put("day", qcDate.toLocalDateTime().getDayOfMonth());
            map.put("qcDate", qcDate);
            map.put("sequenceResults", resultList);
            dayResults.add(map);
        });
        return Result.succ(1, "查询成功", dayResults);
    }
    @Override
    public Result batchDeleteMaterialItemResult(QcResultParam dto) {
        List<QcMaterialItemResult> iqcResults = dto.getIqcResults();
        List<String> ids = iqcResults.stream().map(QcMaterialItemResult::getId).collect(Collectors.toList());
        boolean b = this.removeByIds(ids);
        if (b) {
            return Result.succ(1, "删除成功", null);
        } else {
            return Result.fail(400, "删除失败", "");
        }
    }

    @Autowired
    QcMaterialSummaryMapper qcMaterialSummaryMapper;
    @Override
    public Result findQcMaterialMonthData(QcResultParam dto) {
        //先根据仪器，月份，编号查出对应的质控物信息
        String instrId = dto.getInstrId();
        String qcNo = dto.getQcNo();
        String qcMonth = dto.getQcMonth();//2025-05
        Date today = DateUtil.parse(dto.getQcMonth(), "yyyy-MM");

        String qcNum = dto.getQcNum();
        String qcItemId = dto.getQcItemId();
        if (ToolsUtils.isNotEmpty(instrId) && ToolsUtils.isNotEmpty(qcNo) && ToolsUtils.isNotEmpty(qcMonth)) {
            Map<String, Object> returnMap = new HashMap<>();
            //根据instrId和qcMonth 查出质控总结
            MPJLambdaWrapper<QcMaterialSummary> querySummaryWrapper = new MPJLambdaWrapper<>();
            querySummaryWrapper.selectAll(QcMaterialSummary.class)
                    .eq(QcMaterialSummary::getInstrId,instrId)
                    .eq(QcMaterialSummary::getQcMonth,qcMonth)
                    .eq(QcMaterialSummary::getDelFlag,0);
            List<QcMaterialSummary> qcMaterialSummaries = qcMaterialSummaryMapper.selectList(querySummaryWrapper);
            if(ToolsUtils.isNotEmpty(qcMaterialSummaries)){
                QcMaterialSummary qcMaterialSummary = qcMaterialSummaries.get(0);
                returnMap.put("qcMaterialSummary",qcMaterialSummary);
            }
            MPJLambdaWrapper<QcMaterial> lambdaQueryWrapper = new MPJLambdaWrapper<>();
            lambdaQueryWrapper.selectAll(QcMaterial.class)
                    .selectAs("ins", LabInstrSetup::getInstrName, QcMaterial::getInstrName)
                    .leftJoin(LabInstrSetup.class, "ins", LabInstrSetup::getId, QcMaterial::getInstrId)
                    .eq(QcMaterial::getDelFlag, 0)
                    .eq(QcMaterial::getInstrId, instrId)
                    .eq(QcMaterial::getQcNo, qcNo);
            //启用日期在本月或者早于本月
            //获取qcMonth 月份的最后一天
            lambdaQueryWrapper.le(QcMaterial::getEnableDate, DateUtil.endOfMonth(today).toTimestamp());
            //有效期在当月或者晚于当月
            lambdaQueryWrapper.ge(QcMaterial::getValidDate, DateUtil.beginOfMonth(today).toTimestamp());
            //停用日期为空，或者
            lambdaQueryWrapper.and(wrapper -> wrapper.isNull(QcMaterial::getStopDate).or().ge(QcMaterial::getStopDate, DateUtil.beginOfMonth(today).toTimestamp()));
            List<QcMaterial> qcMaterialList = qcMaterialMapper.selectList(lambdaQueryWrapper);
            if(CollectionUtils.isNotEmpty(qcMaterialList)){
                Map<String, Object> QcMaterialMap = new HashMap<>();
                QcMaterialMap.put("instrName",qcMaterialList.get(0).getInstrName());
                //获取qcMaterialList中的批号以,分割
                String batchNums = qcMaterialList.stream().map(QcMaterial::getBatchNum).collect(Collectors.joining(","));
                QcMaterialMap.put("batchNum",batchNums);
                //根据qcMonth 2025-05 转换为 2025年5月
                QcMaterialMap.put("qcMonthName",DateUtil.format(today, "yyyy年MM月"));
                returnMap.put("qcMaterial",QcMaterialMap);

                //获取质控物id集合
                List<String> qcMaterialIds = qcMaterialList.stream().map(QcMaterial::getId).collect(Collectors.toList());
                //查出这些质控物下，当月的结果
                MPJLambdaWrapper<QcMaterialItemResult> queryWrapper = new MPJLambdaWrapper<>();
                queryWrapper.selectAll(QcMaterialItemResult.class)
                        .selectAs("tg", QcMaterialItemTarget::getCv, QcMaterialItemResult::getCv)
                        .selectAs("ld", LabItemDict::getItemNo, QcMaterialItemResult::getItemDictCode)
                        .selectAs("ld", LabItemDict::getItemNa, QcMaterialItemResult::getItemDictName)
                        .selectAs("ld", LabItemDict::getSeq, QcMaterialItemResult::getItemSeq)
                        .leftJoin(QcMaterialItemTarget.class, "tg", QcMaterialItemTarget::getId, QcMaterialItemResult::getMaterialTargetId)
                        .leftJoin(LabItemDict.class,"ld",p -> p.eq(LabItemDict::getDelFlag,0).eq(LabItemDict::getId,QcMaterialItemResult::getItemDictId))

                        .eq(QcMaterialItemResult::getDelFlag, 0)
                        .ge(QcMaterialItemResult::getQcDate, DateUtil.beginOfMonth(today).toTimestamp())
                        .le(QcMaterialItemResult::getQcDate, DateUtil.endOfMonth(today).toTimestamp())
                        .in(QcMaterialItemResult::getMaterialId, qcMaterialIds);
                if(ToolsUtils.isNotEmpty(qcItemId)){
                    queryWrapper.eq(QcMaterialItemResult::getQcItemId, qcItemId);
                }
                if(ToolsUtils.isNotEmpty(qcNum)){
                    if("best".equals(qcNum)){
                        queryWrapper.eq(QcMaterialItemResult::getIsBestInDay,true);
                    }else if("1".equals(qcNum)||"2".equals(qcNum)||"3".equals(qcNum)){
                        queryWrapper.eq(QcMaterialItemResult::getQcSequence,Integer.valueOf(qcNum) );

                    }
                }
                List<QcMaterialItemResult> materialItemResultList = qcMaterialItemResultMapper.selectList(queryWrapper);
                if(CollectionUtils.isNotEmpty(materialItemResultList)){
                    List<Map<String, Object>> returnList = new ArrayList<>();

                    //根据itemDictId和materialTargetId 分组
                    Map<List<Object>, List<QcMaterialItemResult>> qcResultMap = materialItemResultList.stream().collect(Collectors.groupingBy(s -> Arrays.asList(s.getQcItemId(), s.getMaterialTargetId(),s.getItemSeq())));
                    queryWrapper.orderByAsc(LabItemDict::getSeq);
                    for (List<Object> qcResultlist : qcResultMap.keySet()) {
                        String itemDictId = "";
                        String materialTargetId = "";
                        if (ToolsUtils.isNotEmpty(qcResultlist.get(0))) {
                            itemDictId = String.valueOf(qcResultlist.get(0));
                        }
                        if (ToolsUtils.isNotEmpty(qcResultlist.get(1))) {
                            materialTargetId = String.valueOf(qcResultlist.get(1));
                        }
                        Map<String, Object> returnReulstMap = new HashMap<>();
                        List<QcMaterialItemResult> qcItemResultlist = qcResultMap.get(qcResultlist);
                        returnReulstMap.put("itemDictName",qcItemResultlist.get(0).getItemDictName());
                        returnReulstMap.put("itemDictCode",qcItemResultlist.get(0).getItemDictCode());
                        returnReulstMap.put("targetMean",baoliuxiaoshu(qcItemResultlist.get(0).getMean()));
                        returnReulstMap.put("targetSd",baoliuxiaoshu(qcItemResultlist.get(0).getSd()));
                        returnReulstMap.put("targetCv",baoliuxiaoshu(qcItemResultlist.get(0).getCv()));
                        returnReulstMap.put("itemSeq",qcItemResultlist.get(0).getItemSeq());
                        //计算qcItemResultlist结果的平均值
                        List<Double> rmr = qcItemResultlist.stream().map(QcMaterialItemResult::getNumResult).collect(Collectors.toList());
                        Double resultMean = rmr.stream().mapToDouble(d -> d).average().getAsDouble();
                        returnReulstMap.put("resultMean",baoliuxiaoshu(resultMean));
                        //计算结果的标准差
                        //double resultSd = qcItemResultlist.size() <= 1 ? rmr.get(0) : this.standardDeviation(rmr.stream().mapToDouble(d -> d).toArray());
                        double resultSd = qcItemResultlist.size() <= 1 ? rmr.get(0) : StandardDeviationCalculator.calculateSampleStdDev(rmr);

                        returnReulstMap.put("resultSd", baoliuxiaoshu(resultSd));
                        //计算CV值
                        returnReulstMap.put("resultCv",baoliuxiaoshu((resultSd/resultMean)*100));
                        //次数
                        returnReulstMap.put("resultCnt",qcItemResultlist.size());
                        //失控数
                        long outCnt = qcItemResultlist.stream().filter(QcMaterialItemResult::getIsOutControl).count();
                        returnReulstMap.put("resultOutCnt", outCnt);
                        //在控率 = 在控数/次数 需要显示百分比
                        int resultInCnt = (int) ((qcItemResultlist.size() - outCnt) / (double) qcItemResultlist.size() * 100);                        //转成int类型
                        returnReulstMap.put("resultInCnt",resultInCnt+"%");
                        returnList.add(returnReulstMap);
                    }
                    List<Map<String, Object>> sortedByAge = returnList.stream()
                            .sorted((p1, p2) -> Integer.compare((Integer)p1.get("itemSeq"), (Integer)p2.get("itemSeq"))) // 升序排序
                            .collect(Collectors.toList());
                    returnMap.put("qcMaterialRsultList",sortedByAge);
                }
            }
            return Result.succ(1, "成功", returnMap);

        }else {
            return Result.succ(1, "成功", null);
        }
    }
    private Double baoliuxiaoshu(Double resultMean){
        if(ToolsUtils.isNotEmpty(resultMean)){
            Double parseDouble = Double.parseDouble(new DecimalFormat("#.##").format(resultMean));
            return parseDouble;
        }else {
            return resultMean;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result saveOrUpdateMaterialResult(QcResultParam param) {

        List<QcMaterialItemResult> iqcResults = param.getIqcResults();
        List<QcMaterialItemResult> resultList = iqcResults.stream().filter(m -> ToolsUtils.isNotEmpty(m.getResult())).collect(Collectors.toList());
        //查出结果为空的数据
        List<String> delResultIdList = iqcResults.stream().filter(m -> (ToolsUtils.isEmpty(m.getResult()) && ToolsUtils.isNotEmpty(m.getId()))).map(QcMaterialItemResult::getId).collect(Collectors.toList());
        if(ToolsUtils.isNotEmpty(delResultIdList)){
            this.removeByIds(delResultIdList);
        }


        if(ToolsUtils.isNotEmpty(resultList)){

            String itemDefine = param.getItemDefine();
            Date today = DateUtil.parse(param.getQcMonth(), "yyyy-MM");
            Timestamp beginDate = DateUtil.beginOfMonth(today).toTimestamp();
            Timestamp endDate = DateUtil.endOfMonth(today).toTimestamp();
            String materialItemId = param.getMaterialItemId();
            if("dl".equals(itemDefine)){
                //查询靶值SD值
                LambdaQueryWrapper<QcMaterialItemTarget> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(QcMaterialItemTarget::getDelFlag, 0)
                        .eq(QcMaterialItemTarget::getMaterialItemId, materialItemId)
                        //.le(QcMaterialItemTarget::getEnableDate, beginDate)
                        //.and(wrapper -> wrapper.isNull(QcMaterialItemTarget::getStopDate).or().ge(QcMaterialItemTarget::getStopDate, endDate))
                        .orderByAsc(QcMaterialItemTarget::getSeq);
                List<QcMaterialItemTarget> targetList = qcMaterialItemTargetMapper.selectList(queryWrapper);

                for (QcMaterialItemResult qcResult : resultList) {
                    if (ToolsUtils.isEmpty(qcResult.getId())) {
                        qcResult.setMaterialId(param.getMaterialId());
                        qcResult.setMaterialItemId(param.getMaterialItemId());
                        qcResult.setQcItemId(param.getQcItemId());
                        qcResult.setInstrId(param.getInstrId());
                        qcResult.setItemDictId(param.getItemDictId());
                        qcResult.setNumResult(Double.valueOf(qcResult.getResult()));
                        qcResult.setMethod(param.getMethod());
                        qcResult.setQcNo(param.getQcNo());
                        qcResult.setBatchNum(param.getBatchNum());
                        qcResult.setHorizontal(param.getHorizontal());
                        qcResult.setIsOutControl(false);
                        qcResult.setIsAlarm(false);
                        qcResult.setIsBestInDay(false);
                        qcResult.setIsOutControlHandled(false);
                    } else {
                        qcResult.setNumResult(Double.valueOf(qcResult.getResult()));
                    }
                    if (CollectionUtils.isNotEmpty(targetList)) {
                        Optional<QcMaterialItemTarget> first = targetList.stream().filter(a -> a.getEnableDate().compareTo(qcResult.getQcDate()) <= 0 && (ToolsUtils.isEmpty(a.getStopDate()) || (ToolsUtils.isNotEmpty(a.getStopDate()) && a.getStopDate().after(qcResult.getQcDate())))).findFirst();
                        if(first.isPresent()){
                            QcMaterialItemTarget target = first.get();
                            Double sd = target.getSd();
                            Double mean = target.getMean();
                            qcResult.setMean(mean);
                            qcResult.setSd(sd);
                            qcResult.setMaterialTargetId(target.getId());
                            if(null != sd && null != mean){
                                qcResult.setZ(sd==0?0:(qcResult.getNumResult() - mean) / sd);
                            }
                        }
                    }
                }
                boolean b = this.saveOrUpdateBatch(resultList);
                //批量重新计算某个范围内的质控结果。包括重新设置质控物设置
                this.calcQcRuleManyAsync(param.getMaterialItemId(),param.getQcItemId(),param.getInstrId(),beginDate,endDate);
                if (b) {
                    return Result.succ(1, "成功", null);
                } else {
                    return Result.fail(400, "失败", "");
                }
            }else if ("dx".equals(itemDefine)){
                //查询定性结果值
                LambdaQueryWrapper<QcMaterialItemFixed> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(QcMaterialItemFixed::getDelFlag, 0)
                        .eq(QcMaterialItemFixed::getMaterialItemId, materialItemId)
                        .orderByAsc(QcMaterialItemFixed::getSeq);
                List<QcMaterialItemFixed> fixedList = qcMaterialItemFixedMapper.selectList(queryWrapper);

                for (QcMaterialItemResult qcResult : resultList) {
                    if (ToolsUtils.isEmpty(qcResult.getId())) {
                        qcResult.setMaterialId(param.getMaterialId());
                        qcResult.setMaterialItemId(param.getMaterialItemId());
                        qcResult.setQcItemId(param.getQcItemId());
                        qcResult.setInstrId(param.getInstrId());
                        qcResult.setItemDictId(param.getItemDictId());
                        qcResult.setResult(qcResult.getResult());
                        qcResult.setMethod(param.getMethod());
                        qcResult.setQcNo(param.getQcNo());
                        qcResult.setBatchNum(param.getBatchNum());
                        qcResult.setHorizontal(param.getHorizontal());
                        qcResult.setIsOutControl(false);
                        qcResult.setIsAlarm(false);
                        qcResult.setIsBestInDay(false);
                        qcResult.setIsOutControlHandled(false);
                    }
                    if (CollectionUtils.isNotEmpty(fixedList)) {
                        Optional<QcMaterialItemFixed> first = fixedList.stream()
                                .filter(a -> a.getEnableDate().compareTo(qcResult.getQcDate()) <= 0
                                        && (ToolsUtils.isEmpty(a.getStopDate()) || (ToolsUtils.isNotEmpty(a.getStopDate()) && a.getStopDate().after(qcResult.getQcDate())))
                                        && a.getResult().equals(qcResult.getResult())).findFirst();
                        if(first.isPresent()){
                            QcMaterialItemFixed itemFixed = first.get();
                            qcResult.setNumResult(itemFixed.getNumResult());
                            qcResult.setIsAlarm(itemFixed.getIsAlarm());
                            qcResult.setIsOutControl(itemFixed.getIsOutControl());
                            qcResult.setMaterialTargetId(itemFixed.getId());
                            qcResult.setZ(itemFixed.getZ());
                        }
                    }
                }
                boolean b = this.saveOrUpdateBatch(resultList);
                if (b) {
                    return Result.succ(1, "成功", null);
                } else {
                    return Result.fail(400, "失败", "");
                }
            }else {
                return Result.succ(1, "成功", null);
            }

        }else {
            return Result.succ(1, "成功", null);
        }

    }

    @Override
    public Result getIQCMaterialsInfoAsync(QcPageDto dto) {
        String itemDictId = dto.getItemDictId();
        String materialIds = dto.getMaterialIds();
        String[] split = materialIds.split(",");

        MPJLambdaWrapper<QcMaterialItemResult> queryWrapper = new MPJLambdaWrapper<>();
        queryWrapper
                .selectAs("ma",QcMaterial::getId, QcResultVo::getMaterialId)
                .selectAs("ma",QcMaterial::getMaterialName, QcResultVo::getMaterialName)
                .selectAs("ma",QcMaterial::getBatchNum, QcResultVo::getBatchNum)
                .selectAs("ma",QcMaterial::getHorizontal, QcResultVo::getHorizontal)
                .selectAs("ma",QcMaterial::getManufacturer, QcResultVo::getManufacturer)
                .selectAs("ma",QcMaterial::getEnableDate, QcResultVo::getEnableDate)
                .selectAs("ma",QcMaterial::getValidDate, QcResultVo::getValidDate)
                .selectAs("ta",QcMaterialItemTarget::getId, QcResultVo::getMaterialTargetId)
                .selectAs("ta",QcMaterialItemTarget::getEnableDate, QcResultVo::getTaEnableDate)
                .selectAs("ta",QcMaterialItemTarget::getStopDate, QcResultVo::getStopDate)
                .selectAs("ta",QcMaterialItemTarget::getMean, QcResultVo::getMean)
                .selectAs("ta",QcMaterialItemTarget::getSd, QcResultVo::getSd)
                .selectAs("ta",QcMaterialItemTarget::getCv, QcResultVo::getCv)
                .selectAs("it",QcMaterialItem::getMethod, QcResultVo::getMethod)
                .selectAs(QcMaterialItemResult::getNumResult, QcResultVo::getNumResult)
                .selectAs(QcMaterialItemResult::getResult, QcResultVo::getResult)
                .selectAs(QcMaterialItemResult::getZ, QcResultVo::getZ)
                .leftJoin(QcMaterial.class, "ma", QcMaterial::getId, QcMaterialItemResult::getMaterialId)
                .leftJoin(QcMaterialItem.class, "it", QcMaterialItem::getId, QcMaterialItemResult::getMaterialItemId)
                .leftJoin(QcMaterialItemTarget.class, "ta", QcMaterialItemTarget::getId, QcMaterialItemResult::getMaterialTargetId)
                .eq(QcMaterialItemResult::getItemDictId,itemDictId)
                .in(QcMaterialItemResult::getMaterialId,split)
                .eq(QcMaterialItemResult::getDelFlag,0)
                .ge(QcMaterialItemResult::getQcDate,dto.getQCDateS())
                .le(QcMaterialItemResult::getQcDate,dto.getQCDateE());
        List<QcResultVo> results = qcMaterialItemResultMapper.selectJoinList(QcResultVo.class, queryWrapper);
        List<QcResultVo> ms = results.stream().map(qcResultVo -> {
            QcResultVo qcResultVo1 = new QcResultVo();
            qcResultVo1.setMaterialId(qcResultVo.getMaterialId());
            qcResultVo1.setMaterialName(qcResultVo.getMaterialName());
            qcResultVo1.setBatchNum(qcResultVo.getBatchNum());
            qcResultVo1.setHorizontal(qcResultVo.getHorizontal());
            qcResultVo1.setManufacturer(qcResultVo.getManufacturer());
            qcResultVo1.setEnableDate(qcResultVo.getEnableDate());
            qcResultVo1.setValidDate(qcResultVo.getValidDate());
            qcResultVo1.setMethod(qcResultVo.getMethod());
            return qcResultVo1;
        }).distinct().collect(Collectors.toList());

        List<QcResultVo> mbss = results.stream().filter(o -> Objects.nonNull(o.getMaterialTargetId())).map(qcResultVo -> {
            QcResultVo qcResultVo1 = new QcResultVo();
            qcResultVo1.setMaterialId(qcResultVo.getMaterialId());
            qcResultVo1.setMaterialTargetId(qcResultVo.getMaterialTargetId());
            qcResultVo1.setTaEnableDate(qcResultVo.getTaEnableDate());
            qcResultVo1.setStopDate(qcResultVo.getStopDate());
            qcResultVo1.setMean(qcResultVo.getMean());
            qcResultVo1.setCv(qcResultVo.getCv());
            qcResultVo1.setSd(qcResultVo.getSd());

            return qcResultVo1;
        }).distinct().collect(Collectors.toList());

        List<QcResultVo> rs = results.stream().map(qcResultVo -> {
            QcResultVo qcResultVo1 = new QcResultVo();
            qcResultVo1.setMaterialId(qcResultVo.getMaterialId());
            qcResultVo1.setMaterialTargetId(qcResultVo.getMaterialTargetId());
            qcResultVo1.setNumResult(qcResultVo.getNumResult());
            qcResultVo1.setZ(qcResultVo.getZ());

            return qcResultVo1;
        }).collect(Collectors.toList());
        List<QcMaterialInfoOutputDto> resResults = new ArrayList<>();
        //质控物
        for(QcResultVo m:ms){
            QcMaterialInfoOutputDto rm = new QcMaterialInfoOutputDto();
            BeanUtils.copyProperties(m,rm);
            List<Double> rmr = rs.stream().filter(re -> re.getMaterialId().equals(m.getMaterialId()))
                    .map(QcResultVo::getNumResult).collect(Collectors.toList());

            rm.setMean(rmr.stream().mapToDouble(d->d).average().getAsDouble());
            rm.setSd(results.size()<=1?rmr.get(0):this.standardDeviation(rmr.stream().mapToDouble(d -> d).toArray()));
            rm.setCv(rm.getMean()==0?0:rm.getSd()/rm.getMean());
            Map<String, Object> sv = this.getIQCMaterialStatisticValueAsync(itemDictId, rm.getMaterialId(), dto.getQCDateE());
            rm.setToTalMean(Double.valueOf(String.valueOf(sv.get("mean"))));
            rm.setToTalSd(Double.valueOf(String.valueOf(sv.get("sd"))));
            rm.setToTalCv(Double.valueOf(String.valueOf(sv.get("cv"))));

            //靶值参数
            List<QcResultVo> mmbss = mbss.stream().filter(mb -> mb.getMaterialId().equals(rm.getMaterialId())).collect(Collectors.toList());
            List<QcMaterialTargetDto> targetDtoList = new ArrayList<>();
            for(QcResultVo s: mmbss){
                QcMaterialTargetDto rmbs = new QcMaterialTargetDto();
                rmbs.setMaterialId(s.getMaterialId());
                rmbs.setMaterialTargetId(s.getMaterialTargetId());
                rmbs.setTaEnableDate(s.getTaEnableDate());
                rmbs.setStopDate(s.getStopDate());
                rmbs.setMean(s.getMean());
                rmbs.setSd(s.getSd());
                rmbs.setCv(s.getCv());
                List<Double> rmbsr = rs.stream().filter(rss -> (rss.getMaterialId().equals(m.getMaterialId()) &&ToolsUtils.isNotEmpty(rss.getMaterialTargetId()) && rss.getMaterialTargetId().equals(s.getMaterialTargetId())))
                        .map(QcResultVo::getNumResult).collect(Collectors.toList());
                if(ToolsUtils.isNotEmpty(rmbsr)){
                    rmbs.setToTalMean(rmbsr.stream().mapToDouble(d->d).average().getAsDouble());
                    rmbs.setToTalSd(rmbsr.size()<=1?rmbsr.get(0):this.standardDeviation(rmbsr.stream().mapToDouble(d -> d).toArray()));
                    rmbs.setToTalCv(rmbs.getToTalSd() / rmbs.getToTalMean());
                }
                targetDtoList.add(rmbs);
            }
            rm.setTargetDtoList(targetDtoList);
            resResults.add(rm);
        }
        return Result.succ(1, "成功", resResults);
    }


    @Autowired
    LabRptresultMapper labRptresultMapper;
    @Autowired
    QcInstrSampleMapper qcInstrSampleMapper;
    @Autowired
    QcMaterialMapper qcMaterialMapper;
    @Override
    public Result transToQCResultManyAsync(List<String> labRptresultIds) {
        MPJLambdaWrapper<LabRptresult> queryWrapper = new MPJLambdaWrapper<>();
        queryWrapper
                .selectAs(LabRptresult::getSampleda, QcTranToResultVo::getSampleda)
                .selectAs("ins",LabInstrSetup::getId, QcTranToResultVo::getInstrId)
                .selectAs(LabRptresult::getInstrCode, QcTranToResultVo::getInstrCode)
                .selectAs(LabRptresult::getSampleNo, QcTranToResultVo::getSampleNo)
                .selectAs(LabRptresult::getItemDictId, QcTranToResultVo::getItemDictId)
                .selectAs(LabRptresult::getItemNo, QcTranToResultVo::getItemNo)
                .selectAs(LabRptresult::getRptGroup,QcTranToResultVo::getRptGroup)
                .selectAs(LabRptresult::getTestResult, QcTranToResultVo::getTestResult)
                .selectAs(LabRptresult::getIntResultId,QcTranToResultVo::getIntResultId)
                .selectAs(QcInstrSample::getBatchNum,QcTranToResultVo::getBatchNum)
                .selectAs(QcInstrSample::getHorizontal,QcTranToResultVo::getHorizontal)
                .selectAs("qm",QcMaterial::getId,QcTranToResultVo::getQcMaterialId)
                .selectAs("qm",QcMaterial::getQcNo,QcTranToResultVo::getQcNo)
//                .innerJoin(LabItemDict.class,"dict",p->p.eq(LabItemDict::getItemNo,LabRptresult::getItemNo)
//                        .eq(LabItemDict::getRptGroup,LabRptresult::getRptGroup)
//                        .eq(LabItemDict::getIsQc,1)
//                        .eq(LabItemDict::getDelFlag,0))
                .innerJoin(LabInstrSetup.class,"ins",p->p.eq(LabInstrSetup::getInstrCode,LabRptresult::getInstrCode)
                        .eq(LabInstrSetup::getRptGroup,LabRptresult::getRptGroup)
                        .eq(LabInstrSetup::getDelFlag,0))
                .innerJoin(QcInstrSample.class,"qis",p->p.eq(QcInstrSample::getInstrId,LabInstrSetup::getId)
                        .eq(QcInstrSample::getSampleNo,LabRptresult::getSampleNo)
                        .eq(QcInstrSample::getDelFlag,0))
                .innerJoin(QcMaterial.class,"qm",p->p.eq(QcMaterial::getBatchNum,QcInstrSample::getBatchNum)
                        .eq(QcMaterial::getHorizontal,QcInstrSample::getHorizontal)
                        .eq(QcMaterial::getInstrId,QcInstrSample::getInstrId)
                        .eq(QcMaterial::getDelFlag,0))
                .in(LabRptresult::getId,labRptresultIds) ;
        List<QcTranToResultVo> results = labRptresultMapper.selectJoinList(QcTranToResultVo.class, queryWrapper);

        //循环

        for(QcTranToResultVo vo : results ){
            //根据仪器与标本号 查出对应得质控物
            //根据批次与水平 仪器 查出指控物
            String batchNum = vo.getBatchNum();
            String horizontal = vo.getHorizontal();
            String qcMaterialId = vo.getQcMaterialId();
            String testResult = vo.getTestResult();

            //根据质控物，报告项目 查出质控物下得对应得质控细项
            MPJLambdaWrapper<QcMaterialItem> queryWrapper3 = new MPJLambdaWrapper<>();
            queryWrapper3.selectAll(QcMaterialItem.class)
                    .innerJoin(LabItemDict.class,"dict",p->p.eq(LabItemDict::getId,QcMaterialItem::getItemDictId)
                            .eq(LabItemDict::getRptGroup,vo.getRptGroup())
                            .eq(LabItemDict::getItemNo,vo.getItemNo())
                            .eq(LabItemDict::getDelFlag,0))
                    .eq(QcMaterialItem::getMaterialId,qcMaterialId)
                    .eq(QcMaterialItem::getDelFlag,0);
            List<QcMaterialItem> materialItemList = qcMaterialItemMapper.selectList(queryWrapper3);
            if(ToolsUtils.isNotEmpty(materialItemList)){
                QcMaterialItem materialItem = materialItemList.get(0);
                String itemDefine = materialItem.getItemDefine();
                //dl 定量 走靶值逻辑
                if(itemDefine.equals("dl")){
                    Double numResult = null;
                    try{
                        numResult = Double.valueOf(testResult);
                    }
                    catch (Exception e){
                    }
                    //查询靶值SD值
                    LambdaQueryWrapper<QcMaterialItemTarget> queryWrapper4 = new LambdaQueryWrapper<>();
                    queryWrapper4.eq(QcMaterialItemTarget::getDelFlag, 0)
                            .eq(QcMaterialItemTarget::getMaterialItemId, materialItem.getId())
                            .le(QcMaterialItemTarget::getEnableDate, new Timestamp(new Date().getTime()))
                            .and(wrapper -> wrapper.isNull(QcMaterialItemTarget::getStopDate).or().ge(QcMaterialItemTarget::getStopDate, new Timestamp(new Date().getTime())))
                            .orderByAsc(QcMaterialItemTarget::getSeq);
                    List<QcMaterialItemTarget> targetList = qcMaterialItemTargetMapper.selectList(queryWrapper4);

                    //根据细项与日期查出当前有多少条数据
                    Timestamp sampleda = vo.getSampleda();
                    String format = DateUtil.format(sampleda, "yyyy-MM-dd");
                    Timestamp startTime = Timestamp.valueOf(format + " 00:00:00");
                    Timestamp endTime = Timestamp.valueOf(format + " 23:59:59");
                    LambdaQueryWrapper<QcMaterialItemResult> queryWrapper5 = new LambdaQueryWrapper<>();
                    queryWrapper5.eq(QcMaterialItemResult::getMaterialItemId,materialItem.getId())
                            .ge(QcMaterialItemResult::getQcDate,startTime)
                            .le(QcMaterialItemResult::getQcDate,endTime)
                            .eq(QcMaterialItemResult::getDelFlag,0)
                            .orderByDesc(QcMaterialItemResult::getQcSequence);
                    List<QcMaterialItemResult> itemResults = qcMaterialItemResultMapper.selectList(queryWrapper5);
                    //根据仪器接口ID查看此条数据是否已经插入到仪器结果中
                    if(ToolsUtils.isNotEmpty(itemResults)){
                        Optional<QcMaterialItemResult> first = itemResults.stream().filter(a -> ToolsUtils.isNotEmpty(a.getIntResultId())&&a.getIntResultId().equals(vo.getIntResultId())).findFirst();
                        if(first.isPresent()){
                            QcMaterialItemResult qcResult = first.get();
                            qcResult.setNumResult(numResult);
                            qcResult.setResult(testResult);
                            qcResult.setQcDate(vo.getSampleda());
                            qcResult.setIntResultId(vo.getIntResultId());
                            if (CollectionUtils.isNotEmpty(targetList)) {
                                Double sd = targetList.get(0).getSd();
                                Double mean = targetList.get(0).getMean();
                                qcResult.setMean(mean);
                                qcResult.setSd(sd);
                                qcResult.setMaterialTargetId(targetList.get(0).getId());
                                if(ToolsUtils.isNotEmpty(qcResult.getNumResult())&&ToolsUtils.isNotEmpty(mean)&&ToolsUtils.isNotEmpty(sd)){
                                    qcResult.setZ(sd==0?0:(qcResult.getNumResult() - mean) / sd);
                                }
                            }
                            this.updateById(qcResult);
                        }else {
                            QcMaterialItemResult qcResult = new QcMaterialItemResult();
                            qcResult.setMaterialId(qcMaterialId);
                            qcResult.setMaterialItemId(materialItem.getId());
                            qcResult.setOrgId(materialItem.getOrgId());
                            qcResult.setQcItemId(materialItem.getQcItemId());
                            qcResult.setInstrId(materialItem.getInstrId());
                            qcResult.setItemDictId(materialItem.getItemDictId());
                            qcResult.setMethod(materialItem.getMethod());
                            qcResult.setQcNo(vo.getQcNo());
                            qcResult.setBatchNum(batchNum);
                            qcResult.setHorizontal(horizontal);
                            qcResult.setIsOutControl(false);
                            qcResult.setIsAlarm(false);
                            qcResult.setIsBestInDay(false);
                            qcResult.setIsOutControlHandled(false);
                            qcResult.setNumResult(numResult);
                            qcResult.setResult(testResult);
                            qcResult.setQcDate(vo.getSampleda());
                            qcResult.setIntResultId(vo.getIntResultId());
                            qcResult.setQcSequence(itemResults.get(0).getQcSequence()+1);
                            //qcResult.setQcSequence(1);
                            if (CollectionUtils.isNotEmpty(targetList)) {
                                Double sd = targetList.get(0).getSd();
                                Double mean = targetList.get(0).getMean();
                                qcResult.setMean(mean);
                                qcResult.setSd(sd);
                                qcResult.setMaterialTargetId(targetList.get(0).getId());
                                if(ToolsUtils.isNotEmpty(qcResult.getNumResult())&&ToolsUtils.isNotEmpty(mean)&&ToolsUtils.isNotEmpty(sd)){
                                    qcResult.setZ(sd==0?0:(qcResult.getNumResult() - mean) / sd);
                                }
                            }
                            this.save(qcResult);
                        }

                    }else {
                        QcMaterialItemResult qcResult = new QcMaterialItemResult();
                        qcResult.setMaterialId(qcMaterialId);
                        qcResult.setMaterialItemId(materialItem.getId());
                        qcResult.setQcItemId(materialItem.getQcItemId());
                        qcResult.setOrgId(materialItem.getOrgId());
                        qcResult.setInstrId(materialItem.getInstrId());
                        qcResult.setItemDictId(materialItem.getItemDictId());
                        qcResult.setMethod(materialItem.getMethod());
                        qcResult.setQcNo(vo.getQcNo());
                        qcResult.setBatchNum(batchNum);
                        qcResult.setHorizontal(horizontal);
                        qcResult.setIsOutControl(false);
                        qcResult.setIsAlarm(false);
                        qcResult.setIsBestInDay(false);
                        qcResult.setIsOutControlHandled(false);
                        qcResult.setNumResult(numResult);
                        qcResult.setResult(testResult);
                        qcResult.setQcDate(vo.getSampleda());
                        qcResult.setIntResultId(vo.getIntResultId());
                        qcResult.setQcSequence(1);
                        if (CollectionUtils.isNotEmpty(targetList)) {
                            Double sd = targetList.get(0).getSd();
                            Double mean = targetList.get(0).getMean();
                            qcResult.setMean(mean);
                            qcResult.setSd(sd);
                            qcResult.setMaterialTargetId(targetList.get(0).getId());
                            if(ToolsUtils.isNotEmpty(qcResult.getNumResult())&&ToolsUtils.isNotEmpty(mean)&&ToolsUtils.isNotEmpty(sd)){
                                qcResult.setZ(sd==0?0:(qcResult.getNumResult() - mean) / sd);
                            }
                        }
                        this.save(qcResult);

                    }
                    //批量重新计算某个范围内的质控结果。包括重新设置质控物设置
                    String formatTest = DateUtil.format(sampleda, "yyyy-MM");
                    Date today = DateUtil.parse(formatTest, "yyyy-MM");
                    Timestamp beginDate = DateUtil.beginOfMonth(today).toTimestamp();
                    Timestamp endDate = DateUtil.endOfMonth(today).toTimestamp();
                    this.calcQcRuleManyAsync(materialItem.getId(),materialItem.getQcItemId(),materialItem.getInstrId(),beginDate,endDate);

                }else if(itemDefine.equals("dx")){
                    //dx 定性 走定性结果逻辑
                    //查询定性值
                    LambdaQueryWrapper<QcMaterialItemFixed> queryWrapper4 = new LambdaQueryWrapper<>();
                    queryWrapper4.eq(QcMaterialItemFixed::getDelFlag, 0)
                            .eq(QcMaterialItemFixed::getMaterialItemId, materialItem.getId())
                            .le(QcMaterialItemFixed::getEnableDate, new Timestamp(new Date().getTime()))
                            .eq(QcMaterialItemFixed::getResult,vo.getTestResult())
                            .and(wrapper -> wrapper.isNull(QcMaterialItemFixed::getStopDate).or().ge(QcMaterialItemFixed::getStopDate, new Timestamp(new Date().getTime())))
                            .orderByAsc(QcMaterialItemFixed::getSeq);
                    List<QcMaterialItemFixed> fixedList = qcMaterialItemFixedMapper.selectList(queryWrapper4);
                    //根据细项与日期查出当前有多少条数据
                    Timestamp sampleda = vo.getSampleda();
                    String format = DateUtil.format(sampleda, "yyyy-MM-dd");
                    Timestamp startTime = Timestamp.valueOf(format + " 00:00:00");
                    Timestamp endTime = Timestamp.valueOf(format + " 23:59:59");
                    LambdaQueryWrapper<QcMaterialItemResult> queryWrapper5 = new LambdaQueryWrapper<>();
                    queryWrapper5.eq(QcMaterialItemResult::getMaterialItemId,materialItem.getId())
                            .ge(QcMaterialItemResult::getQcDate,startTime)
                            .le(QcMaterialItemResult::getQcDate,endTime)
                            .eq(QcMaterialItemResult::getDelFlag,0)
                            .orderByDesc(QcMaterialItemResult::getQcSequence);
                    List<QcMaterialItemResult> itemResults = qcMaterialItemResultMapper.selectList(queryWrapper5);


                    //根据仪器接口ID查看此条数据是否已经插入到仪器结果中
                    if(ToolsUtils.isNotEmpty(itemResults)){
                        Optional<QcMaterialItemResult> first = itemResults.stream().filter(a -> ToolsUtils.isNotEmpty(a.getIntResultId())&&a.getIntResultId().equals(vo.getIntResultId())).findFirst();
                        if(first.isPresent()){
                            QcMaterialItemResult qcResult = first.get();
                            qcResult.setResult(testResult);
                            qcResult.setQcDate(vo.getSampleda());
                            qcResult.setIntResultId(vo.getIntResultId());
                            if (CollectionUtils.isNotEmpty(fixedList)) {
                                qcResult.setNumResult(fixedList.get(0).getNumResult());
                                qcResult.setIsAlarm(fixedList.get(0).getIsAlarm());
                                qcResult.setIsOutControl(fixedList.get(0).getIsOutControl());
                                qcResult.setMaterialTargetId(fixedList.get(0).getId());
                                qcResult.setZ(fixedList.get(0).getZ());
                            }
                            this.updateById(qcResult);
                        }else {
                            QcMaterialItemResult qcResult = new QcMaterialItemResult();
                            qcResult.setMaterialId(qcMaterialId);
                            qcResult.setMaterialItemId(materialItem.getId());
                            qcResult.setOrgId(materialItem.getOrgId());
                            qcResult.setQcItemId(materialItem.getQcItemId());
                            qcResult.setInstrId(materialItem.getInstrId());
                            qcResult.setItemDictId(materialItem.getItemDictId());
                            qcResult.setMethod(materialItem.getMethod());
                            qcResult.setQcNo(vo.getQcNo());
                            qcResult.setBatchNum(batchNum);
                            qcResult.setHorizontal(horizontal);
                            qcResult.setIsOutControl(false);
                            qcResult.setIsAlarm(false);
                            qcResult.setIsBestInDay(false);
                            qcResult.setIsOutControlHandled(false);
                            qcResult.setResult(testResult);
                            qcResult.setQcDate(vo.getSampleda());
                            qcResult.setIntResultId(vo.getIntResultId());
                            qcResult.setQcSequence(itemResults.get(0).getQcSequence()+1);
                            //qcResult.setQcSequence(1);
                            if (CollectionUtils.isNotEmpty(fixedList)) {
                                qcResult.setNumResult(fixedList.get(0).getNumResult());
                                qcResult.setIsAlarm(fixedList.get(0).getIsAlarm());
                                qcResult.setIsOutControl(fixedList.get(0).getIsOutControl());
                                qcResult.setMaterialTargetId(fixedList.get(0).getId());
                                qcResult.setZ(fixedList.get(0).getZ());
                            }
                            this.save(qcResult);
                        }

                    }else {
                        QcMaterialItemResult qcResult = new QcMaterialItemResult();
                        qcResult.setMaterialId(qcMaterialId);
                        qcResult.setMaterialItemId(materialItem.getId());
                        qcResult.setQcItemId(materialItem.getQcItemId());
                        qcResult.setOrgId(materialItem.getOrgId());
                        qcResult.setInstrId(materialItem.getInstrId());
                        qcResult.setItemDictId(materialItem.getItemDictId());
                        qcResult.setMethod(materialItem.getMethod());
                        qcResult.setQcNo(vo.getQcNo());
                        qcResult.setBatchNum(batchNum);
                        qcResult.setHorizontal(horizontal);
                        qcResult.setIsOutControl(false);
                        qcResult.setIsAlarm(false);
                        qcResult.setIsBestInDay(false);
                        qcResult.setIsOutControlHandled(false);
                        qcResult.setResult(testResult);
                        qcResult.setQcDate(vo.getSampleda());
                        qcResult.setIntResultId(vo.getIntResultId());
                        qcResult.setQcSequence(1);
                        if (CollectionUtils.isNotEmpty(fixedList)) {
                            qcResult.setNumResult(fixedList.get(0).getNumResult());
                            qcResult.setIsAlarm(fixedList.get(0).getIsAlarm());
                            qcResult.setIsOutControl(fixedList.get(0).getIsOutControl());
                            qcResult.setMaterialTargetId(fixedList.get(0).getId());
                            qcResult.setZ(fixedList.get(0).getZ());
                        }
                        this.save(qcResult);

                    }

                }



            }



        }

        return Result.succ(1, "转质控成功", null);
    }

    @Override
    public Result transToQcResultByMainInfo(QcResultAddParam qcResultAddParam) {
        //设置过质控参数的项目才转
        MPJLambdaWrapper<LabRptresult> queryWrapper = new MPJLambdaWrapper<>();
        queryWrapper.selectAll(LabRptresult.class)
                .selectAs(LabInstrSetup::getId,LabRptresult::getInstrId)
                .innerJoin(LabInstrSetup.class,"ins", p->p.eq(LabInstrSetup::getInstrCode,LabRptresult::getInstrCode).eq(LabInstrSetup::getDelFlag,0))
                .innerJoin(LabItemDict.class,"dict",p->p.eq(LabItemDict::getItemNo,LabRptresult::getItemNo).eq(LabItemDict::getRptGroup,LabRptresult::getRptGroup).eq(LabItemDict::getDelFlag,0))
                .innerJoin(QcInstrSample.class,"sa",p->p.eq(QcInstrSample::getSampleNo,LabRptresult::getSampleNo).eq(QcInstrSample::getInstrId,LabInstrSetup::getId).eq(QcInstrSample::getDelFlag,0))
                .innerJoin(QcMaterial.class,"qm",p->p.eq(QcMaterial::getBatchNum,QcInstrSample::getBatchNum).eq(QcMaterial::getHorizontal,QcInstrSample::getHorizontal).eq(QcMaterial::getInstrId,QcInstrSample::getInstrId).eq(QcMaterial::getDelFlag,0))
//                .innerJoin(QcMaterialItemTarget.class,"tar",
//                        p->p.eq(QcMaterialItemTarget::getMaterialId,QcMaterial::getId).eq(QcMaterialItemTarget::getItemDictId,LabItemDict::getId)
//                                .eq(QcMaterialItemTarget::getInstrId,LabInstrSetup::getId)
//                                .le(QcMaterialItemTarget::getEnableDate, new Timestamp(new Date().getTime()))
//                                .and(wrapper -> wrapper.isNull(QcMaterialItemTarget::getStopDate).or().ge(QcMaterialItemTarget::getStopDate, new Timestamp(new Date().getTime())))
//                                .eq(QcMaterialItemTarget::getDelFlag,0))
                .eq(LabRptresult::getDelFlag,0)
                .eq(LabRptresult::getMaininfoId,qcResultAddParam.getMaininfoId());
        List<LabRptresult> rptresultList = labRptresultMapper.selectList(queryWrapper);
        List<String> collect = rptresultList.stream().map(LabRptresult::getId).distinct().collect(Collectors.toList());

        if(ToolsUtils.isNotEmpty(collect)){
            //根据报告项目和仪器 查出
            return transToQCResultManyAsync(collect);

        }else {
            return Result.fail( "无质控项目");
        }
    }

    @Override
    public Result transToQcResultByRptResultId(QcResultAddParam qcResultAddParam) {
        List<String> collect = new ArrayList<>();
        collect.add(qcResultAddParam.getRptResultId());

        if(ToolsUtils.isNotEmpty(collect)){
            //根据报告项目和仪器 查出
            return transToQCResultManyAsync(collect);

        }else {
            return Result.fail( "不是质控项目或无靶值");
        }
    }

    @Override
    public Result QcInstrItemRuleUpdateAsync(String materialItemId, String qcItemId, String instrId, Timestamp beginDate, Timestamp endDate) {
        this.calcQcRuleManyAsync(materialItemId,qcItemId,instrId,beginDate,endDate);
        return Result.succ("");
    }

    @Override
    public Result getQcResultListByQcMaterial(QcResultTimeParam qcResultTimeParam) {
        //根据质控物id和质控日期 查质控结果
        String materialId = qcResultTimeParam.getMaterialId();
        String qcDate = qcResultTimeParam.getQcDate();
        // 查询结果数据
        Date curryDay;
        try {
            curryDay = DateUtils.parseDate(qcResultTimeParam.getQcDate(), "yyyy-MM-dd");
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
        MPJLambdaWrapper<QcMaterialItemResult> lambdaQueryWrapper = new MPJLambdaWrapper();
        lambdaQueryWrapper.selectAll(QcMaterialItemResult.class)
                .selectAs("ld", LabItemDict::getItemNo, QcMaterialItemResult::getItemDictCode)
                .selectAs("ld", LabItemDict::getItemNa, QcMaterialItemResult::getItemDictName)
                .innerJoin(LabItemDict.class,"ld",p -> p.eq(LabItemDict::getDelFlag,0).eq(LabItemDict::getId,QcMaterialItemResult::getItemDictId))

                .eq(QcMaterialItemResult::getMaterialId, qcResultTimeParam.getMaterialId())
                .eq(QcMaterialItemResult::getDelFlag, 0)
                .ge(QcMaterialItemResult::getQcDate, DateUtil.beginOfDay(curryDay).toTimestamp())
                .le(QcMaterialItemResult::getQcDate, DateUtil.endOfDay(curryDay).toTimestamp())
                .orderByAsc(LabItemDict::getSeq);

        List<QcMaterialItemResult> qcMaterialItemResults = qcMaterialItemResultMapper.selectList(lambdaQueryWrapper);

        return Result.succ(1, "查询成功", qcMaterialItemResults);
    }

    private Map<String,Object> getIQCMaterialStatisticValueAsync(String itemDictId,String materialId,Timestamp QCDateE){
        Map<String,Object> result = new HashMap<>();
        result.put("mean",0);
        result.put("sd",0);
        result.put("cv",0);
        MPJLambdaWrapper<QcMaterialItemResult> queryWrapper = new MPJLambdaWrapper<>();
        queryWrapper
                .selectAs(QcMaterialItemResult::getMaterialId, QcResultVo::getMaterialId)
                .selectAs(QcMaterialItemResult::getNumResult, QcResultVo::getNumResult)
                .leftJoin(QcMaterial.class, "ma", QcMaterial::getId, QcMaterialItemResult::getMaterialId)
                .eq(QcMaterialItemResult::getItemDictId,itemDictId)
                .le(QcMaterialItemResult::getQcDate,QCDateE)
                .eq(QcMaterialItemResult::getMaterialId,materialId)
                .eq(QcMaterialItemResult::getDelFlag,0);
        List<QcResultVo> resultList = qcMaterialItemResultMapper.selectJoinList(QcResultVo.class, queryWrapper);
        List<QcResultVo> results = resultList.stream().filter(a -> ToolsUtils.isNotEmpty(a.getNumResult())).collect(Collectors.toList());
        if(CollectionUtils.isNotEmpty(results)){
            result.put("mean",results.stream().mapToDouble(QcResultVo::getNumResult).average().getAsDouble());
            result.put("sd",results.size()<=1?results.get(0).getNumResult():this.standardDeviation(results.stream().mapToDouble(QcResultVo::getNumResult).toArray()));
            result.put("cv",Double.valueOf(String.valueOf(result.get("mean"))) ==0?0:Double.valueOf(String.valueOf(result.get("sd")))/Double.valueOf(String.valueOf(result.get("mean"))));
        }
        return  result;
    }

    private double standardDeviation(double[] data){
        // 计算平均值
        double mean = Arrays.stream(data).average().orElse(Double.NaN);
        // 计算每个数值与平均值的差的平方
        double sumOfSquares = Arrays.stream(data).map(x -> Math.pow(x - mean, 2)).sum();
        // 计算方差
        double variance = sumOfSquares / data.length;
        // 计算标准差
        double standardDeviation = Math.sqrt(variance);
        return standardDeviation;
    }

    private void calcQcRuleManyAsync(String materialItemId,String qcItemId,String instrId, Timestamp beginDate, Timestamp endDate) {
        //查出质控结果111
        LambdaQueryWrapper<QcMaterialItemResult> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(QcMaterialItemResult::getDelFlag,0)
                .eq(QcMaterialItemResult::getMaterialItemId,materialItemId)
                .ge(QcMaterialItemResult::getQcDate,beginDate)
                .le(QcMaterialItemResult::getQcDate,endDate)
                .orderByAsc(QcMaterialItemResult::getQcDate,QcMaterialItemResult::getQcSequence);
        List<QcMaterialItemResult> iqcResults = qcMaterialItemResultMapper.selectList(queryWrapper);

        //初始化质控规则
        LambdaQueryWrapper<QcRule> queryWrapper1 = new LambdaQueryWrapper<>();
        queryWrapper1.eq(QcRule::getDelFlag,0);
        List<QcRule> ruleList = qcRuleMapper.selectList(queryWrapper1);
        RuleManager ruleManager = new RuleManager();
        for(QcRule rule : ruleList){
            ruleManager.Register(RuleType.valueOf(rule.getRuleType()), rule.getId(), rule.getName(), Integer.parseInt(rule.getAcnt()) , Integer.parseInt(rule.getNcnt()), Double.parseDouble(rule.getLcnt()));
        }

        //获取质控规则设置信息
        List<QcRuleAttribute> dbRuleAttributeList =this.getQCRuleAttributeManyAsync(qcItemId,instrId);

        //计算质控规则
        //结果失控规则列表
        List<QCResultRule> resultRuleList = new ArrayList<>();
        //计算质控规则
        for(int i=0;i<iqcResults.size();i++){
            QcMaterialItemResult iqcResult = iqcResults.get(i);
            List<QCResult> results = this.toQcResult(iqcResults.stream().limit(i + 1).collect(Collectors.toList()),iqcResult.getId());

            ruleManager.EvaluateMulti(results);

            //更新失控规则等信息
            List<com.ysd.lis.util.qcrule.Result> ocResults = new ArrayList<>();

            for(QCResult result : results){
                ocResults.addAll( result.getResults().stream().filter(re -> ToolsUtils.isNotEmpty(re.getOutControlInfos())).collect(Collectors.toList()));
            }
            Optional<com.ysd.lis.util.qcrule.Result> first = ocResults.stream().filter(oc -> oc.getId().equals(iqcResult.getId())).findFirst();
            if(first.isPresent()){
                com.ysd.lis.util.qcrule.Result ocResult =first.get();
                if(ToolsUtils.isNotEmpty(ocResult)){
                    //获取质控规则设置信息
                    for(OutControlInfo oc : ocResult.getOutControlInfos()){
                        Optional<QcRuleAttribute> first1 = dbRuleAttributeList.stream().filter(db -> db.getId().equals(oc.getRuleId())).findFirst();
                        if(first1.isPresent()){
                            QcRuleAttribute dbRuleAttribute = first1.get();
                            //后期如果将失控记录记录下来 则需要在这里创建记录
                            QCResultRule qcResultRule = new QCResultRule();
                            qcResultRule.setQcResultId(iqcResult.getId());
                            qcResultRule.setQcRuleId(oc.getRuleId());
                            qcResultRule.setIsMutiMaterial(oc.getIsMutiMaterial());
                            qcResultRule.setIsAlarm(dbRuleAttribute.getIsAlarm());
                            qcResultRule.setIsOutControl(dbRuleAttribute.getIsOutControl());
                            qcResultRule.setIsEnabled(true);
                            resultRuleList.add(qcResultRule);

                        }
                    }
                }
            }
            //修改警告、失控标志
            iqcResult.setIsAlarm(false);
            iqcResult.setAlarmRules("");

            iqcResult.setIsOutControl(false);
            iqcResult.setOutControlRules("");
            List<QCResultRule> mainresultRuleList = resultRuleList.stream().filter(p -> (p.getIsAlarm() || p.getIsOutControl()) && p.getQcResultId().equals(iqcResult.getId())).collect(Collectors.toList());

            if(CollectionUtils.isNotEmpty(mainresultRuleList)){
                List<String> alarmIds  = mainresultRuleList.stream().filter(ma -> ma.getIsAlarm()).map(QCResultRule::getQcRuleId).distinct().collect(Collectors.toList());
                List<QcRule> alarmRuleList = ruleList.stream().filter(ru -> alarmIds.contains(ru.getId())).collect(Collectors.toList());
                iqcResult.setIsAlarm(alarmRuleList.stream().findAny().isPresent());

                StringBuffer alarmRuleNameBuffer = new StringBuffer();
                for(int j = 0;j<alarmRuleList.size();j++){
                    alarmRuleNameBuffer.append(alarmRuleList.get(j).getName());
                    if(j<alarmRuleList.size()-1){
                        alarmRuleNameBuffer.append(",");
                    }
                }
                iqcResult.setAlarmRules(alarmRuleNameBuffer.toString());
            }


            if(CollectionUtils.isNotEmpty(mainresultRuleList)){
                List<String> outIds  = mainresultRuleList.stream().filter(ma -> ma.getIsOutControl()).map(QCResultRule::getQcRuleId).distinct().collect(Collectors.toList());
                List<QcRule> outRuleList = ruleList.stream().filter(ru -> outIds.contains(ru.getId())).collect(Collectors.toList());
                iqcResult.setIsOutControl(outRuleList.stream().findAny().isPresent());

                StringBuffer outRuleNameBuffer = new StringBuffer();
                for(int j = 0;j<outRuleList.size();j++){
                    outRuleNameBuffer.append(outRuleList.get(j).getName());
                    if(j<outRuleList.size()-1){
                        outRuleNameBuffer.append(",");
                    }
                }
                iqcResult.setOutControlRules(outRuleNameBuffer.toString());
            }

        }
        for(QcMaterialItemResult iqcResult:  iqcResults){
            if(null != iqcResult.getZ()){
                iqcResult.setIsBestInDay(false);
                Optional<QcMaterialItemResult> first = iqcResults.stream().filter(re -> re.getQcDate().equals(iqcResult.getQcDate())&&ToolsUtils.isNotEmpty(re.getZ()))
                        .sorted(Comparator.comparing(QcMaterialItemResult::getIsOutControl)
                                .thenComparing(QcMaterialItemResult::getIsAlarm))
                        .sorted((p1, p2) -> (int) (Math.abs(p1.getZ()) - Math.abs(p2.getZ())))
                        .findFirst();
                if(first.isPresent()){
                    String bestId = first.get().getId();
                    if (bestId == iqcResult.getId())
                    {
                        iqcResult.setIsBestInDay(true);
                    }

                }
            }
        }
        this.updateBatchById(iqcResults);
    }

    private List<QCResult> toQcResult(List<QcMaterialItemResult> qcResults, String mainResultID) {
        List<QCResult> results = new ArrayList<>();
        QCResult qcResult = new QCResult();
        qcResult.setIsMainQCMaterial(true);
        for(QcMaterialItemResult mResult :qcResults){
            com.ysd.lis.util.qcrule.Result result = new com.ysd.lis.util.qcrule.Result();
            result.setId(mResult.getId());
            result.setQCMaterialId(mResult.getMaterialId());
            result.setQCBatchNo(mResult.getId());
            if(null != mResult.getMean()){
                result.setMean(mResult.getMean());
            }
            if(null != mResult.getSd()){
                result.setSd(mResult.getSd());
            }
            if(ToolsUtils.isNotEmpty(mResult.getNumResult())){
                result.setNumResult(mResult.getNumResult());
            }
            if(ToolsUtils.isNotEmpty(mResult.getZ())){
                result.setZvalue(mResult.getZ());//zvalue =(sd==0?0:(numResult - mean) / sd);
            }
            qcResult.getResults().add(result);
        }
        results.add(qcResult);
        return results;
    }

    private List<QcRuleAttribute> getQCRuleAttributeManyAsync(String qcItemId, String instrId) {
        List<QcRuleAttribute> attributeList = new ArrayList<>();

        //先根据仪器质控项目ID查出所有有效期内的质控规则
        LambdaQueryWrapper<QcInstrItemRule>  queryWrapper1 = new LambdaQueryWrapper<>();
        queryWrapper1.eq(QcInstrItemRule::getDelFlag,0)
                .eq(QcInstrItemRule::getPid,qcItemId)
                .eq(QcInstrItemRule::getPtype,"2")
                .le(QcInstrItemRule::getEnableDate, new Timestamp(new Date().getTime()))
                .and(wrapper -> wrapper.isNull(QcInstrItemRule::getStopDate).or().ge(QcInstrItemRule::getStopDate, new Timestamp(new Date().getTime())));
        List<QcInstrItemRule> qcInstrItemRuleList = qcInstrItemRuleMapper.selectList(queryWrapper1);
        if(ToolsUtils.isNotEmpty(qcInstrItemRuleList)){
            for(QcInstrItemRule itemRule : qcInstrItemRuleList){
                String ruleIds = itemRule.getRuleIds();
                String[] split = ruleIds.split(",");
                    for(String sp : split){
                        QcRuleAttribute attribute= new QcRuleAttribute();
                        attribute.setId(sp);
                        attribute.setIsOutControl(itemRule.getIsOutControl());
                        attribute.setIsAlarm(itemRule.getIsAlarm());
                        attribute.setQcItemId(itemRule.getId());
                        attribute.setInstrId(instrId);
                        attributeList.add(attribute);
                    }
            }

        }else {
            //如果空则取仪器下的质控规则
            LambdaQueryWrapper<QcInstrItemRule>  queryWrapper2 = new LambdaQueryWrapper<>();
            queryWrapper2.eq(QcInstrItemRule::getDelFlag,0)
                    .eq(QcInstrItemRule::getPid,instrId)
                    .eq(QcInstrItemRule::getPtype,"1")
                    .le(QcInstrItemRule::getEnableDate, new Timestamp(new Date().getTime()))
                    .and(wrapper -> wrapper.isNull(QcInstrItemRule::getStopDate).or().ge(QcInstrItemRule::getStopDate, new Timestamp(new Date().getTime())));
            List<QcInstrItemRule> qcInstrItemRuleList2 = qcInstrItemRuleMapper.selectList(queryWrapper2);

            for(QcInstrItemRule itemRule : qcInstrItemRuleList2){
                String ruleIds = itemRule.getRuleIds();
                String[] split = ruleIds.split(",");
                for(String sp : split){
                    QcRuleAttribute attribute= new QcRuleAttribute();
                    attribute.setId(sp);
                    attribute.setIsOutControl(itemRule.getIsOutControl());
                    attribute.setIsAlarm(itemRule.getIsAlarm());
                    attribute.setInstrId(itemRule.getId());
                    attributeList.add(attribute);
                }
            }

        }


        return  attributeList;

    }

    private void IsOutControlByMaterialItem(QcMaterialItemResult materialItemResult){
        //1.根据仪器项目id查出20条连续的检测结果项目
        LambdaQueryWrapper<QcMaterialItemResult> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(QcMaterialItemResult::getDelFlag,0);
        queryWrapper.eq(QcMaterialItemResult::getMaterialItemId,materialItemResult.getMaterialItemId());
        queryWrapper.le(QcMaterialItemResult::getQcDate, materialItemResult.getQcDate());
        queryWrapper.orderByDesc(QcMaterialItemResult::getQcDate,QcMaterialItemResult::getQcSequence);
        Page<QcMaterialItemResult> page = new Page<>();
        page.setCurrent(0);
        page.setSize(20);
        Page<QcMaterialItemResult> itemPage = qcMaterialItemResultMapper.selectPage(page, queryWrapper);
        List<QcMaterialItemResult> collect = itemPage.getRecords();
        List<QcMaterialItemResult> executeItemList = collect.stream().sorted(Comparator.comparing(a -> a.getCreateTime())).collect(Collectors.toList());

        //先根据仪器质控项目ID查出所有有效期内的质控规则
        LambdaQueryWrapper<QcInstrItemRule>  queryWrapper1 = new LambdaQueryWrapper<>();
        queryWrapper1.eq(QcInstrItemRule::getDelFlag,0)
                .eq(QcInstrItemRule::getPid,materialItemResult.getQcItemId())
                .eq(QcInstrItemRule::getPtype,"2")
                .le(QcInstrItemRule::getEnableDate, new Timestamp(new Date().getTime()));
        List<QcInstrItemRule> iRuleList = qcInstrItemRuleMapper.selectList(queryWrapper1);
        List<QcInstrItemRule> itemRuleList = iRuleList.stream().filter(ta -> (ToolsUtils.isEmpty(ta.getStopDate()) || (ToolsUtils.isNotEmpty(ta.getStopDate()) && ta.getStopDate().after(new Timestamp(new Date().getTime()))))).collect(Collectors.toList());

        //取出所有的质控规则ID集合
        List<String> ruleIdsList;
        List<String> ruleIdList = new ArrayList<>();
        if(ToolsUtils.isNotEmpty(itemRuleList)){
            ruleIdsList = itemRuleList.stream().map(QcInstrItemRule::getRuleIds).collect(Collectors.toList());
        }else {
            //如果空则取仪器下的质控规则
            LambdaQueryWrapper<QcInstrItemRule>  queryWrapper2 = new LambdaQueryWrapper<>();
            queryWrapper2.eq(QcInstrItemRule::getDelFlag,0)
                    .eq(QcInstrItemRule::getPid,materialItemResult.getInstrId())
                    .eq(QcInstrItemRule::getPtype,"1")
                    .le(QcInstrItemRule::getEnableDate, new Timestamp(new Date().getTime()));
            List<QcInstrItemRule> iRuleList2 = qcInstrItemRuleMapper.selectList(queryWrapper2);
            List<QcInstrItemRule> itemRuleList2 = iRuleList2.stream().filter(ta -> (ToolsUtils.isEmpty(ta.getStopDate()) || (ToolsUtils.isNotEmpty(ta.getStopDate()) && ta.getStopDate().after(new Timestamp(new Date().getTime()))))).collect(Collectors.toList());

            ruleIdsList = itemRuleList2.stream().map(QcInstrItemRule::getRuleIds).collect(Collectors.toList());
        }
        for(String ruleIds : ruleIdsList){
            String[] split = ruleIds.split(",");
            for(String sp : split){
                ruleIdList.add(sp);
            }
        }
        RuleManager ruleManager = new RuleManager();
        //3.查出对应的质控规则
        List<String> ruleIds = ruleIdList.stream().distinct().collect(Collectors.toList());
        List<QcRule> ruleList = qcRuleMapper.selectBatchIds(ruleIds);
        for(QcRule rule : ruleList){
            RuleType type = RuleType.valueOf(rule.getRuleType());
            ruleManager.Register(type, rule.getId(), rule.getName(), Integer.parseInt(rule.getAcnt()) , Integer.parseInt(rule.getNcnt()), Double.parseDouble(rule.getLcnt()));
        }

        for (Rule r : ruleManager.getRules())
        {
            System.out.println(r.getName()+"  "+r.getId());
        }
        //查出有几种质控物
        Map<String, List<QcMaterialItemResult>> materialMap = executeItemList.stream().collect(Collectors.groupingBy(QcMaterialItemResult::getMaterialId));
        Set<String> materialIds = materialMap.keySet();
        List<QCResult> qcDatas = new ArrayList<>();
        for(String materialId:materialIds){
            QCResult qcData0 = new QCResult();
            List<com.ysd.lis.util.qcrule.Result> results = qcData0.getResults();
            List<QcMaterialItemResult> resultList = materialMap.get(materialId);
            for(QcMaterialItemResult result : resultList){
                com.ysd.lis.util.qcrule.Result r = new com.ysd.lis.util.qcrule.Result();
                r.setId(result.getId());
                r.setQCBatchNo(result.getId());
                r.setMean(result.getMean().doubleValue());
                r.setSd(result.getSd().doubleValue());
                r.setNumResult(result.getNumResult().doubleValue());
                if(ToolsUtils.isNotEmpty(result.getZ())){
                    r.setZvalue(result.getZ().doubleValue());//zvalue =(sd==0?0:(numResult - mean) / sd);
                }
                results.add(r);
            }
            if(materialId.equals(materialItemResult.getMaterialId())){
                qcData0.setIsMainQCMaterial(true);
            }else {
                qcData0.setIsMainQCMaterial(false);
            }
            qcDatas.add(qcData0);
        }
        List<RuleType> types = ruleManager.EvaluateMulti(qcDatas);

        for (QCResult qcData : qcDatas)
        {
            Boolean isOutControl = false;
            List<String> outRuleIds = new ArrayList<>();

            System.out.println("QCMaterial: "+qcData.getIsMainQCMaterial());
            for (com.ysd.lis.util.qcrule.Result iitem : qcData.getResults())
            {
                System.out.println(iitem.getId() + "   " + iitem.getQCBatchNo() + "     " + iitem.getZvalue());
//                if(qcData.getIsMainQCMaterial()&&iitem.getIsOutControl()){
//                    isOutControl="0";
//                }

                if (iitem.getIsOutControl())
                {
                    isOutControl= true;
                    StringBuffer sb = new StringBuffer() ;
                    for(OutControlInfo o : iitem.getOutControlInfos())
                    {
                        outRuleIds.add(o.getRuleId());
                        System.out.println("OC:" + o.getRuleId() +"  "+ o.getIsMutiMaterial() + " Members:" + o.getMemberIds());
                    }
                }


            }
            StringBuffer outRuleNameBuffer = new StringBuffer();
            List<String> distOutRuleIds = outRuleIds.stream().distinct().collect(Collectors.toList());
            for(int i = 0;i<distOutRuleIds.size();i++){
                String s = distOutRuleIds.get(i);
                outRuleNameBuffer.append(ruleList.stream().filter(a -> a.getId().equals(s)).map(QcRule::getName).findFirst().get()) ;
                if(i<distOutRuleIds.size()-1){
                    outRuleNameBuffer.append(",");
                }
            }

            LambdaUpdateWrapper<QcMaterialItemResult> updateWrapper = new LambdaUpdateWrapper();
            updateWrapper.eq(QcMaterialItemResult::getId,materialItemResult.getId())
                    .set(QcMaterialItemResult::getIsOutControl,isOutControl)
                    .set(QcMaterialItemResult::getOutControlRules,(outRuleNameBuffer.toString()));
            this.update(updateWrapper);


        }


    }
}
